package cn.org.seventh.common;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

/**
 * DTO代理工具 该类可以将任意包含标准Bean读写方法的接口代理成一个该接口的实现类
 * 
 * @author Seventh
 * 
 * @param <T>
 */
public class BeanProxy<T> implements InvocationHandler, Serializable {
	private static final long serialVersionUID = 1L;

	private Map<String, Object> properties;
	private String className;

	private BeanProxy(Class<T> type) throws Exception {
		properties = new HashMap<String, Object>();
		className = type.getName();
		BeanInfo beanInfo = Introspector.getBeanInfo(type);
		PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
		for (PropertyDescriptor descriptor : descriptors) {
			properties.put(descriptor.getName(), null);
		}
	}

	@SuppressWarnings("unchecked")
	public static <V> V getProxy(Class<V> type) {
		try {
			return (V) Proxy.newProxyInstance(BeanProxy.class.getClassLoader(),
					new Class[] { type, Map.class }, new BeanProxy<V>(type));
		} catch (Exception e) {
			throw new RuntimeException("not a bean type!");
		}
	}

	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		BeanInfo beanInfo = Introspector.getBeanInfo(proxy.getClass());
		PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
		for (PropertyDescriptor descriptor : descriptors) {
			if (null != descriptor.getWriteMethod()
					&& method.getName().equals(
							descriptor.getWriteMethod().getName())) {
				properties.put(descriptor.getName(), args[0]);
				return null;
			}
			if (null != descriptor.getReadMethod()
					&& method.getName().equals(
							descriptor.getReadMethod().getName())) {
				return properties.get(descriptor.getName());
			}
		}
		if ("toString".equals(method.getName())) {
			return toString();
		}
		return null;
	}

	@Override
	public String toString() {
		return properties.toString() + "@" + className;
	}
}
